home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / The World of Computer Software.iso / jp4dpmi.zip / JCMAIN.C < prev    next >
C/C++ Source or Header  |  1992-12-14  |  13KB  |  490 lines

  1. /*
  2.  * jcmain.c
  3.  *
  4.  * Copyright (C) 1991, 1992, Thomas G. Lane.
  5.  * This file is part of the Independent JPEG Group's software.
  6.  * For conditions of distribution and use, see the accompanying README file.
  7.  *
  8.  * This file contains an MS-DOS user interface for the JPEG compressor.
  9.  * It follows DOS practice more closely than does the standard jcmain.c.
  10.  * This compiles under Borland C or Microsoft C; don't know about others.
  11.  * If you are using jmemnobs.c, #define FREE_MEM_ESTIMATE=0.
  12.  */
  13.  
  14. #include "jinclude.h"
  15. #include <stdlib.h>        /* to declare exit() */
  16. #include <string.h>        /* string functions */
  17. #include <signal.h>        /* to declare signal() */
  18.  
  19. #ifdef __TURBOC__
  20.                 /* Borland C declarations */
  21. #include <dir.h>        /* for findfirst/findnext */
  22. typedef struct ffblk FF_DATA;
  23. #define FF_NAME  ff_name
  24. #define FINDFIRST(path,blk)    findfirst(path, blk, 0)
  25. #define FINDNEXT(blk)        findnext(blk)
  26.  
  27. #include <alloc.h>        /* for farcoreleft() */
  28. /* In Borland C, force -m equal to 95% of farcoreleft figure */
  29. #define FREE_MEM_ESTIMATE  ((farcoreleft() * 95L) / 100L)
  30.  
  31. #else /* !__TURBOC__ */
  32.                 /* Microsoft C declarations */
  33. #include <dos.h>        /* for findfirst/findnext */
  34. typedef struct find_t FF_DATA;
  35. #define FF_NAME  name
  36. #define FINDFIRST(path,blk)    _dos_findfirst(path, _A_NORMAL, blk)
  37. #define FINDNEXT(blk)        _dos_findnext(blk)
  38.  
  39. #endif /* __TURBOC__ */
  40.  
  41. #define READ_BINARY    "rb"
  42. #define WRITE_BINARY    "wb"
  43.  
  44. #ifndef EXIT_FAILURE        /* define exit() codes if not provided */
  45. #define EXIT_FAILURE  1
  46. #endif
  47. #ifndef EXIT_SUCCESS
  48. #define EXIT_SUCCESS  0
  49. #endif
  50.  
  51.  
  52. #include "jversion.h"        /* for version message */
  53.  
  54.  
  55. /*
  56.  * PD version of getopt(3).
  57.  */
  58.  
  59. #include "egetopt.c"
  60.  
  61.  
  62. /*
  63.  * This routine determines what format the input file is,
  64.  * and selects the appropriate input-reading module.
  65.  *
  66.  * To determine which family of input formats the file belongs to,
  67.  * we may look only at the first byte of the file, since C does not
  68.  * guarantee that more than one character can be pushed back with ungetc.
  69.  * Looking at additional bytes would require one of these approaches:
  70.  *     1) assume we can fseek() the input file (fails for piped input);
  71.  *     2) assume we can push back more than one character (works in
  72.  *        some C implementations, but unportable);
  73.  *     3) provide our own buffering as is done in djpeg (breaks input readers
  74.  *        that want to use stdio directly, such as the RLE library);
  75.  * or  4) don't put back the data, and modify the input_init methods to assume
  76.  *        they start reading after the start of file (also breaks RLE library).
  77.  * #1 is attractive for MS-DOS but is untenable on Unix.
  78.  *
  79.  * The most portable solution for file types that can't be identified by their
  80.  * first byte is to make the user tell us what they are.  This is also the
  81.  * only approach for "raw" file types that contain only arbitrary values.
  82.  * We presently apply this method for Targa files.  Most of the time Targa
  83.  * files start with 0x00, so we recognize that case.  Potentially, however,
  84.  * a Targa file could start with any byte value (byte 0 is the length of the
  85.  * seldom-used ID field), so we accept a -T switch to force Targa input mode.
  86.  */
  87.  
  88. static boolean is_targa;    /* records user -T switch */
  89.  
  90.  
  91. LOCAL void
  92. select_file_type (compress_info_ptr cinfo)
  93. {
  94.   int c;
  95.  
  96.   if (is_targa) {
  97. #ifdef TARGA_SUPPORTED
  98.     jselrtarga(cinfo);
  99. #else
  100.     ERREXIT(cinfo->emethods, "Targa support was not compiled");
  101. #endif
  102.     return;
  103.   }
  104.  
  105.   if ((c = getc(cinfo->input_file)) == EOF)
  106.     ERREXIT(cinfo->emethods, "Empty input file");
  107.  
  108.   switch (c) {
  109. #ifdef GIF_SUPPORTED
  110.   case 'G':
  111.     jselrgif(cinfo);
  112.     break;
  113. #endif
  114. #ifdef PPM_SUPPORTED
  115.   case 'P':
  116.     jselrppm(cinfo);
  117.     break;
  118. #endif
  119. #ifdef RLE_SUPPORTED
  120.   case 'R':
  121.     jselrrle(cinfo);
  122.     break;
  123. #endif
  124. #ifdef TARGA_SUPPORTED
  125.   case 0x00:
  126.     jselrtarga(cinfo);
  127.     break;
  128. #endif
  129.   default:
  130. #ifdef TARGA_SUPPORTED
  131.     ERREXIT(cinfo->emethods, "Unrecognized input file format --- if it's Targa, use -T");
  132. #else
  133.     ERREXIT(cinfo->emethods, "Unrecognized input file format");
  134. #endif
  135.     break;
  136.   }
  137.  
  138.   if (ungetc(c, cinfo->input_file) == EOF)
  139.     ERREXIT(cinfo->emethods, "ungetc failed");
  140. }
  141.  
  142.  
  143. /*
  144.  * This routine gets control after the input file header has been read.
  145.  * It must determine what output JPEG file format is to be written,
  146.  * and make any other compression parameter changes that are desirable.
  147.  */
  148.  
  149. METHODDEF void
  150. c_ui_method_selection (compress_info_ptr cinfo)
  151. {
  152.   /* If the input is gray scale, generate a monochrome JPEG file. */
  153.   if (cinfo->in_color_space == CS_GRAYSCALE)
  154.     j_monochrome_default(cinfo);
  155.   /* For now, always select JFIF output format. */
  156. #ifdef JFIF_SUPPORTED
  157.   jselwjfif(cinfo);
  158. #else
  159.   You shoulda defined JFIF_SUPPORTED.   /* deliberate syntax error */
  160. #endif
  161. }
  162.  
  163.  
  164. /*
  165.  * Signal catcher to ensure that temporary files are removed before aborting.
  166.  * Doesn't do anything during intervals where emethods is NULL.
  167.  */
  168.  
  169. static external_methods_ptr emethods; /* for access to free_all */
  170.  
  171. LOCAL void
  172. signal_catcher (int signum)
  173. {
  174.   if (emethods != NULL) {
  175.     emethods->trace_level = 0;    /* turn off trace output */
  176.     (*emethods->free_all) ();    /* clean up memory allocation & temp files */
  177.   }
  178.   exit(EXIT_FAILURE);
  179. }
  180.  
  181.  
  182. LOCAL void
  183. usage (void)
  184. /* complain about bad command line */
  185. {
  186.   fprintf(stderr, "Usage: cjpeg [switches] inputfile(s)\n");
  187.   fprintf(stderr, "List of input files may use wildcards (* and ?)\n");
  188.   fprintf(stderr, "If input file name has no extension, .GIF is assumed\n");
  189.   fprintf(stderr, "Output filename is same as input filename, but extension .JPG\n");
  190.   fprintf(stderr, "Optional switches:\n");
  191.   fprintf(stderr, "  -Q quality  Image quality (0..100; 5-95 is useful range)\n");
  192.   fprintf(stderr, "  -o          Optimize Huffman table (smaller file, but slow)\n");
  193. #ifdef TARGA_SUPPORTED
  194.   fprintf(stderr, "  -T          Input file is Targa format (usually not needed)\n");
  195. #endif
  196. #ifndef FREE_MEM_ESTIMATE
  197.   fprintf(stderr, "  -m memory   Maximum memory to use (default 300K); see USAGE\n");
  198. #endif
  199.   fprintf(stderr, "  -d          Generate debug output\n");
  200.   exit(EXIT_FAILURE);
  201. }
  202.  
  203.  
  204. /* Display progress report */
  205.  
  206. METHODDEF void
  207. progress_monitor (compress_info_ptr cinfo, long loopcounter, long looplimit)
  208. {
  209.   if (cinfo->total_passes > 1) {
  210.     fprintf(stderr, "\rPass %d/%d: %3d%% ",
  211.         cinfo->completed_passes+1, cinfo->total_passes,
  212.         (int) (loopcounter*100L/looplimit));
  213.   } else {
  214.     fprintf(stderr, "\r %3d%% ",
  215.         (int) (loopcounter*100L/looplimit));
  216.   }
  217.   fflush(stderr);
  218. }
  219.  
  220.  
  221. /*
  222.  * Check for overwrite of an existing file; clear it with user
  223.  */
  224.  
  225. LOCAL boolean
  226. is_write_ok (char * outfilename)
  227. {
  228.   FILE * ofile;
  229.   int ch;
  230.  
  231.   ofile = fopen(outfilename, READ_BINARY);
  232.   if (ofile == NULL)
  233.     return TRUE;        /* not present */
  234.   fclose(ofile);        /* oops, it is present */
  235.  
  236.   for (;;) {
  237.     fprintf(stderr, "%s already exists, overwrite it? [y/n] ",
  238.         outfilename);
  239.     fflush(stderr);
  240.     ch = getc(stdin);
  241.     fflush(stdin);        /* flush rest of line */
  242.     switch (ch) {
  243.     case 'Y':
  244.     case 'y':
  245.       return TRUE;
  246.     case 'N':
  247.     case 'n':
  248.       return FALSE;
  249.     /* otherwise, ask again */
  250.     }
  251.   }
  252. }
  253.  
  254.  
  255. /*
  256.  * Save current switch values here --- necessary for multiple input files!
  257.  */
  258.  
  259. static int cur_quality;        /* -Q  */
  260. static boolean cur_opt;        /* -o */
  261. static long cur_mem;        /* -m, or value of FREE_MEM_ESTIMATE */
  262. static int cur_trace;        /* -d */
  263.  
  264. #define MAX_FNAME_LEN 150
  265.  
  266.  
  267. /*
  268.  * Process a single input file
  269.  */
  270.  
  271. LOCAL void
  272. process_one_file (char * infilename)
  273. {
  274.   struct Compress_info_struct cinfo;
  275.   struct Compress_methods_struct c_methods;
  276.   struct External_methods_struct e_methods;
  277.   char outfilename[MAX_FNAME_LEN];
  278.   int i;
  279.  
  280.   /* Select the input and output files */
  281.  
  282.   if ((cinfo.input_file = fopen(infilename, READ_BINARY)) == NULL) {
  283.     fprintf(stderr, "cjpeg: can't open %s\n", infilename);
  284.     return;
  285.   }
  286.   /* Make outfilename be infilename with .JPG substituted for extension */
  287.   strcpy(outfilename, infilename);
  288.   for (i = strlen(outfilename)-1; i >= 0; i--) {
  289.     switch (outfilename[i]) {
  290.     case ':':
  291.     case '/':
  292.     case '\\':
  293.       i = 0;            /* stop scanning */
  294.       break;
  295.     case '.':
  296.       outfilename[i] = '\0';    /* lop off existing extension */
  297.       i = 0;            /* stop scanning */
  298.       break;
  299.     default:
  300.       break;            /* keep scanning */
  301.     }
  302.   }
  303.   strcat(outfilename, ".JPG");
  304.  
  305.   if (! is_write_ok(outfilename)) {
  306.     fclose(cinfo.input_file);
  307.     return;
  308.   }
  309.   if ((cinfo.output_file = fopen(outfilename, WRITE_BINARY)) == NULL) {
  310.     fprintf(stderr, "cjpeg: can't create %s\n", outfilename);
  311.     fclose(cinfo.input_file);
  312.     return;
  313.   }
  314.  
  315.   /* Initialize the system-dependent method pointers. */
  316.   cinfo.methods = &c_methods;
  317.   cinfo.emethods = &e_methods;
  318.   jselerror(&e_methods);    /* error/trace message routines */
  319.   jselmemmgr(&e_methods);    /* memory allocation routines */
  320.   c_methods.c_ui_method_selection = c_ui_method_selection;
  321.  
  322.   /* Now OK to enable signal catcher. */
  323.   emethods = &e_methods;
  324.  
  325.   /* Set up JPEG parameters. */
  326.   j_c_defaults(&cinfo, cur_quality, FALSE);
  327.   cinfo.optimize_coding = cur_opt;
  328.   e_methods.max_memory_to_use = cur_mem;
  329.   e_methods.trace_level = cur_trace;
  330.  
  331.   /* Start up progress display, unless trace output is on */
  332.   fprintf(stderr, "Compressing %s => %s\n", infilename, outfilename);
  333.   if (cur_trace == 0)
  334.     c_methods.progress_monitor = progress_monitor;
  335.  
  336.   /* Figure out the input file format, and set up to read it. */
  337.   select_file_type(&cinfo);
  338.  
  339.   /* Do it to it! */
  340.   jpeg_compress(&cinfo);
  341.  
  342.   /* Clear away progress display */
  343.   if (cur_trace == 0)
  344.     fprintf(stderr, "\r                \r");
  345.   fflush(stderr);
  346.  
  347.   /* All done. Close files, disable signal catcher */
  348.   fclose(cinfo.input_file);
  349.   fclose(cinfo.output_file);
  350.   emethods = NULL;
  351. }
  352.  
  353.  
  354. /*
  355.  * Process one filespec; expand wildcards
  356.  */
  357.  
  358. LOCAL void
  359. process_filespec (char * filespec)
  360. {
  361.   FF_DATA ffblock;
  362.   char infilename[MAX_FNAME_LEN];
  363.   int pathlen, i;
  364.   boolean hasext;
  365.  
  366.   /* Determine whether there is a path and an extension */
  367.   pathlen = 0;
  368.   hasext = FALSE;
  369.   for (i = strlen(filespec)-1; i >= 0; i--) {
  370.     switch (filespec[i]) {
  371.     case ':':
  372.     case '/':
  373.     case '\\':
  374.       pathlen = i+1;        /* path part ends here */
  375.       i = 0;            /* stop scanning */
  376.       break;
  377.     case '.':
  378.       hasext = TRUE;        /* there is an extension */
  379.       break;
  380.     default:
  381.       break;            /* keep scanning */
  382.     }
  383.   }
  384.  
  385.   /* Make infilename be filespec; if no explicit extension, attach .GIF */
  386.   strcpy(infilename, filespec);
  387.   if (! hasext)
  388.     strcat(infilename, ".GIF");
  389.  
  390.   /* Scan over matching files */
  391.   if (FINDFIRST(infilename, &ffblock) != 0) {
  392.     fprintf(stderr, "No files matching %s\n", infilename);
  393.   } else {
  394.     do {
  395.       strcpy(infilename, filespec);
  396.       strcpy(infilename + pathlen, ffblock.FF_NAME);
  397.       process_one_file(infilename);
  398.     } while (FINDNEXT(&ffblock) == 0);
  399.   }
  400. }
  401.  
  402.  
  403. /*
  404.  * The main program.
  405.  */
  406.  
  407. GLOBAL int
  408. main (int argc, char **argv)
  409. {
  410.   int c;
  411.  
  412.   /* Set up signal catcher. */
  413.   emethods = NULL;
  414.   signal(SIGINT, signal_catcher);
  415. #ifdef SIGTERM            /* not all systems have SIGTERM */
  416.   signal(SIGTERM, signal_catcher);
  417. #endif
  418.  
  419.   /* Scan command line, process switches */
  420.  
  421.   is_targa = FALSE;        /* initialize default switch values */
  422.   cur_quality = 75;
  423.   cur_opt = FALSE;
  424. #ifdef FREE_MEM_ESTIMATE
  425.   cur_mem = FREE_MEM_ESTIMATE;
  426. #else
  427.   cur_mem = 300000L;        /* reasonable default for DOS */
  428. #endif
  429.   cur_trace = 0;
  430.   
  431.   while ((c = egetopt(argc, argv, "Q:Tom:d")) != EOF)
  432.     switch (c) {
  433.     case 'Q':            /* Quality factor. */
  434.       if (optarg == NULL)
  435.     usage();
  436.       if (sscanf(optarg, "%d", &cur_quality) != 1)
  437.     usage();
  438.       break;
  439.     case 'T':            /* Input file is Targa format. */
  440.       is_targa = TRUE;
  441.       break;
  442.     case 'o':            /* Enable entropy parm optimization. */
  443. #ifdef ENTROPY_OPT_SUPPORTED
  444.       cur_opt = TRUE;
  445. #else
  446.       fprintf(stderr, "cjpeg: sorry, entropy optimization was not compiled\n");
  447.       exit(EXIT_FAILURE);
  448. #endif
  449.       break;
  450.     case 'm':            /* Maximum memory in Kb (or Mb with 'm'). */
  451.       { long lval;
  452.     char ch = 'x';
  453.  
  454.     if (optarg == NULL)
  455.       usage();
  456.     if (sscanf(optarg, "%ld%c", &lval, &ch) < 1)
  457.       usage();
  458.     if (ch == 'm' || ch == 'M')
  459.       lval *= 1000L;
  460.     cur_mem = lval * 1000L;
  461.       }
  462.       break;
  463.     case 'd':            /* Debugging. */
  464.       /* On first -d, print version identification */
  465.       if (cur_trace == 0)
  466.     fprintf(stderr, "Independent JPEG Group's CJPEG, version %s\n%s\n",
  467.         JVERSION, JCOPYRIGHT);
  468.       cur_trace++;
  469.       break;
  470.     case '?':
  471.     default:
  472.       usage();
  473.       break;
  474.     }
  475.  
  476.   /* Process file specifications */
  477.   if (optind >= argc)
  478.     usage();            /* no filespecs?? */
  479.  
  480.   while (optind < argc)
  481.     process_filespec(argv[optind++]);
  482.  
  483.   /* All done. */
  484.   exit(EXIT_SUCCESS);
  485.   return 0;            /* suppress no-return-value warnings */
  486. }
  487.  
  488. void
  489. txtmode(){}
  490.